package com.hm.hall.commons.filter;

import com.alibaba.fastjson.JSONObject;
import com.hm.hall.commons.util.IPUtil;
import org.apache.commons.io.IOUtils;
import org.apache.commons.lang.StringUtils;
import org.apache.log4j.Logger;
import org.springframework.core.annotation.Order;
import org.springframework.http.HttpStatus;
import org.springframework.web.filter.OncePerRequestFilter;
import org.springframework.web.util.ContentCachingRequestWrapper;
import org.springframework.web.util.ContentCachingResponseWrapper;
import org.springframework.web.util.WebUtils;

import javax.servlet.FilterChain;
import javax.servlet.ServletException;
import javax.servlet.annotation.WebFilter;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.net.URI;
import java.net.URISyntaxException;
import java.util.Enumeration;
import java.util.Objects;

/**
 * 注册过滤器
 */
@Order(0)
@WebFilter(filterName = "RequestResponseLogFilter", urlPatterns = "/*")
public class LogFilter extends OncePerRequestFilter {

    private static final String IGNORE_CONTENT_TYPE = "multipart/form-data";
    private static final Logger log = Logger.getLogger(LogFilter.class);

    public static String getRequestParamJSON(HttpServletRequest request) {
        StringBuilder handlerInfo = new StringBuilder("");
        @SuppressWarnings("rawtypes")
        Enumeration en = request.getParameterNames();
        if (en != null) {
            handlerInfo.append("{");
            while (en.hasMoreElements()) {
                String paramName = (String) en.nextElement();
                handlerInfo.append(" \"").append(paramName).append("\":\"").append(request.getParameter(paramName)).append("\",");
            }
            handlerInfo.deleteCharAt(handlerInfo.length() - 1);
            if (handlerInfo.length() > 0) {
                handlerInfo.append("}");
            }
        }
        return handlerInfo.toString();
    }

    @Override
    protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain)
            throws ServletException, IOException {
        if (!isRequestValid(request)) {
            filterChain.doFilter(request, response);
            return;
        }
        if (!(request instanceof ContentCachingRequestWrapper)) {
            request = new ContentCachingRequestWrapper(request);
        }
        if (!(response instanceof ContentCachingResponseWrapper)) {
            response = new ContentCachingResponseWrapper(response);
        }
        int status = HttpStatus.INTERNAL_SERVER_ERROR.value();
        long startTime = System.currentTimeMillis();
        try {
            filterChain.doFilter(request, response);
            status = response.getStatus();
        } finally {
            String path = request.getRequestURI();
            if (!Objects.equals(IGNORE_CONTENT_TYPE, request.getContentType())) {
                StringBuilder handlerInfo = new StringBuilder("===========================================================================》");
                handlerInfo.append(String.format("\n[url:%s] [IP:%s] [handler:%s][time:%sms]\t%s\t%s", path, IPUtil.getIpAddr(request), request.getMethod(), System.currentTimeMillis() - startTime, getRequestParamJSON(request), getRequestBody(request)));
                String result = getResponseBody(response);
                if (StringUtils.isNotBlank(result) && (result.charAt(0) == '{' || result.charAt(0) == '[')) {
                    handlerInfo.append(String.format("\n<--\t\trespons\t\t[%s]\t\t%s", Thread.currentThread().getName(), result));
                }
                log.info(handlerInfo.toString());
            }
            updateResponse(response);
        }
    }

    private boolean isRequestValid(HttpServletRequest request) {
        try {
            new URI(request.getRequestURL().toString());
            return true;
        } catch (URISyntaxException ex) {
            return false;
        }
    }

    private String getRequestBody(HttpServletRequest request) {
        String requestBody = "";
        ContentCachingRequestWrapper wrapper = WebUtils.getNativeRequest(request, ContentCachingRequestWrapper.class);
        if (wrapper != null) {
            try {
                requestBody = IOUtils.toString(wrapper.getContentAsByteArray(), wrapper.getCharacterEncoding());
                if (JSONObject.isValidObject(requestBody)) {
                    requestBody = JSONObject.parse(requestBody).toString();
                }
            } catch (IOException e) {
                // NOOP
            }
        }
        return requestBody;
    }

    private String getResponseBody(HttpServletResponse response) {
        String responseBody = "";
        ContentCachingResponseWrapper wrapper = WebUtils.getNativeResponse(response, ContentCachingResponseWrapper.class);
        if (wrapper != null) {
            try {
                responseBody = IOUtils.toString(wrapper.getContentAsByteArray(), wrapper.getCharacterEncoding());
                if (JSONObject.isValidObject(responseBody)) {
                    responseBody = JSONObject.parse(responseBody).toString();
                }
            } catch (IOException e) {
                // NOOP
            }
        }
        return responseBody;
    }

    private void updateResponse(HttpServletResponse response) throws IOException {
        ContentCachingResponseWrapper responseWrapper = WebUtils.getNativeResponse(response, ContentCachingResponseWrapper.class);
        Objects.requireNonNull(responseWrapper).copyBodyToResponse();
    }

}
